package com.android.lovely.room

import com.android.lovely.api.fetchBotCardsListCase
import com.android.lovely.api.getBotDetailUseCase
import com.android.lovely.framework.doContinuousOperation
import com.android.lovely.room.BotCacheService.convertToUniqueId
import com.android.lovely.user.UserManager
import com.kotlin.extensions.coroutine.CoroutineExt.launchSafety
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit

interface BotRepository {
    suspend fun getBot(id: Int): CacheProfile?
    //    fun CoroutineScope.observeBot(id: Int, block: suspend (CacheProfile) -> Unit)
    suspend fun updateBot(id: Int, updateAction: CacheProfile.() -> Unit)
    fun isBotLoading(id: Int): Boolean
    fun doesBotExist(id: Int): Boolean
    fun refreshBot(id: Int)
    fun updateBotsFromList(bots: List<CacheProfile>)
    fun getCache(id: Int): CacheEntry?
}

data class CacheEntry(
    val bot: CacheProfile,
    val timestamp: Long = System.currentTimeMillis(),
    val isFromServer: Boolean = false
)

class BotRepositoryImpl(
    private val profileDao: ProfileDao,
    private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO),
    private val cacheTimeout: Long = TimeUnit.MINUTES.toMillis(5)
) : BotRepository {

    private val botCache = ConcurrentHashMap<String, CacheEntry>()
    private val ongoingRequestJobs = mutableMapOf<String, Job>()

    override suspend fun getBot(id: Int): CacheProfile? {
        val uniqueId = id.convertToUniqueId()
        val cacheEntry = botCache[uniqueId]

        return when {
            cacheEntry != null && !isCacheExpired(cacheEntry) -> {
                cacheEntry.bot
            }
            else -> {

                val cacheProfile =
                    botCache[uniqueId]?.bot ?: ProfileDatabase.dao.queryProfile(uniqueId)

                if (cacheProfile != null) {
                    // 如果数据库有数据，更新内存缓存

                    cacheProfile
                } else {
//                    // 如果数据库没有数据，从服务器获取
//                    refreshBot(id)
//                    // 等待数据获取完成
//                    withContext(Dispatchers.IO) {
//                        profileDao.queryProfile(uniqueId)
//                    }
                    null
                }
            }
        }
    }

    override suspend fun updateBot(id: Int, updateAction: CacheProfile.() -> Unit) {
        val uniqueId = id.convertToUniqueId()
        val bot = getBot(id) ?: return
        bot.apply(updateAction)
        // 更新数据库
        withContext(Dispatchers.IO) {
            profileDao.insert(bot)
        }
    }

    override fun isBotLoading(id: Int): Boolean {
        return ongoingRequestJobs[id.convertToUniqueId()]?.isActive == true
    }

    override fun doesBotExist(id: Int): Boolean {
        val uniqueId = id.convertToUniqueId()
        return botCache[uniqueId]?.let { !isCacheExpired(it) } == true ||
                profileDao.hasProfile(uniqueId) > 0
    }

    override fun refreshBot(id: Int) {
        val uniqueId = id.convertToUniqueId()
        if (ongoingRequestJobs[uniqueId]?.isActive == true) return

        ongoingRequestJobs[uniqueId] = coroutineScope.launchSafety {
            try {
                val remoteBotDeferred = async {
                    doContinuousOperation { getBotDetailUseCase(id).toCache() }
                }
                val cardsDeferred = async {
                    doContinuousOperation {
                        fetchBotCardsListCase(id, 1, 10).cardList
                    }
                }

                val remoteBot = remoteBotDeferred.await() ?: return@launchSafety
                val cards = cardsDeferred.await()
                remoteBot.cards = cards.orEmpty()

                // 更新内存缓存，标记为服务器数据
                botCache[uniqueId] = CacheEntry(remoteBot, isFromServer = true)

                // 更新数据库
                profileDao.insert(remoteBot)
            } finally {
                ongoingRequestJobs.remove(uniqueId)
            }
        }
    }

    override fun updateBotsFromList(bots: List<CacheProfile>) {
//        bots.forEach { bot ->
//            val uniqueId = bot.id.convertToUniqueId()
//            // 更新内存缓存
//            botCache[uniqueId] = CacheEntry(bot)
//            // 异步更新数据库
//            coroutineScope.launchSafety(Dispatchers.IO) {
//                profileDao.insert(bot)
//            }
//        }
    }

    override fun getCache(id: Int): CacheEntry? {
        return botCache[id.convertToUniqueId()]
    }

    private fun isCacheExpired(entry: CacheEntry): Boolean {
        // 如果是服务器数据，不设置过期时间
        if (entry.isFromServer) return false
        return System.currentTimeMillis() - entry.timestamp > cacheTimeout
    }
}

// 全局角色缓存服务
object BotCacheService {
    private val repository: BotRepository = BotRepositoryImpl(ProfileDatabase.dao)
    private val viewModelScope = CoroutineScope(Dispatchers.Main)

    fun Int.convertToUniqueId(): String = "${UserManager.userInfo?.id},$this"

//    // CoroutineScope 扩展函数
//    fun CoroutineScope.observeBot(id: Int, block: suspend (CacheProfile) -> Unit) {
//        repository.observeBot(id, block)
//    }

    fun CoroutineScope.observeBot(id: Int, block: suspend (CacheProfile) -> Unit){
        val cacheEntry = repository.getCache(id)
        launchSafety {
            ProfileDatabase.dao.queryProfileFlow(id.convertToUniqueId()).collectLatest {
                block.invoke(it ?: return@collectLatest)
            }
        }
        if (cacheEntry != null) {
            return
        }
        refreshBot(id)
    }

    fun updateBot(id: Int, updateAction: CacheProfile.() -> Unit) {
        viewModelScope.launchSafety {
            repository.updateBot(id, updateAction)
        }
    }

    fun refreshBot(id: Int) {
        viewModelScope.launchSafety {
            repository.refreshBot(id)
        }
    }


    fun isBotLoading(id: Int) = repository.isBotLoading(id)

    fun doesBotExist(id: Int) = repository.doesBotExist(id)
}